package com.devin;

import com.beust.jcommander.JCommander;
import com.devin.utils.ClassUtils;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * 用于命令的构建、解析和执行。
 *
 * @author devin
 */
public class Commander {
    private static final Commander INSTANCE;
    private JCommander jCommander;
    /**
     * 用于缓存所有的命令。
     */
    private Map<String, ICommand> commandMap = new LinkedHashMap<>();

    static {
        INSTANCE = new Commander();
        JCommander.Builder builder = JCommander.newBuilder();
        // 扫描和构建命令
        scanCommands( builder );
        INSTANCE.jCommander = builder.build();
    }

    private Commander() {}

    public static Commander build() {
        return INSTANCE;
    }

    /**
     * 解析并执行命令
     *
     * @param args 命令及其参数
     */
    public void execute( String[] args ) {
        INSTANCE.jCommander.parse(args);
        String parsedCommand = INSTANCE.jCommander.getParsedCommand();
        parsedCommand = parsedCommand == null ? "default" : parsedCommand;
        if (!INSTANCE.commandMap.get(parsedCommand).execute()) {
            INSTANCE.jCommander.usage();
        }
    }

    /**
     * 扫描、构建命令
     *
     * @param builder 命令构造器
     */
    private static void scanCommands( JCommander.Builder builder ) {
        Set<Class<?>> commands = ClassUtils.scan("com.devin.command");
        if (commands == null || commands.isEmpty()) {
            System.err.println("未找到命令");
            System.exit(-1);
        }

        for ( Class<?> command : commands ) {

            Object obj = null;
            try {
                obj = command.newInstance();
            } catch ( InstantiationException | IllegalAccessException e ) {
                e.printStackTrace();
            }

            if (!(obj instanceof ICommand)) {
                continue;
            }

            ICommand iCommand = (ICommand) obj;
            String commandName = iCommand.getName();
            if ("default".equals(commandName)) {
                builder.addObject(obj);
            } else {
                builder.addCommand(commandName, obj);
            }
            INSTANCE.commandMap.put(commandName, iCommand);
        }
    }
}
